using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Globalization;
using System.Net.Sockets;
using System.Net;

namespace Microsoft.Office.Project.EPMSync.Config.Library
{
    public class Tracing : IDisposable
    {
        private const int _maxLogFileSize = 131072;

        private System.IO.FileStream _traceLog;
        private System.Diagnostics.TextWriterTraceListener _fileTraceListener;
        private System.Diagnostics.TraceSwitch _TraceSwitch;
        private string _currentFileName = string.Empty;
        private string _coreFileName = "EPMSync-Trace";

        private NetTraceListener _netTraceListener;
        private UdpClient _client;
        private IPEndPoint _endPoint;

        private SyncState _processingState = SyncState.Unknown;

        public Tracing() { }

        public Tracing(string coreFileName, TraceLevel tracingLevel)
        {
            _coreFileName = coreFileName;
            SetTraceLevel(tracingLevel);
        }

        public Tracing(TraceLevel tracingLevel)
        {
            SetTraceLevel(tracingLevel);
        }


        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                Trace.Listeners.Remove(_netTraceListener);
                Trace.Listeners.Remove(_fileTraceListener);
                _netTraceListener.Dispose();
                _fileTraceListener.Close();
                _fileTraceListener.Dispose();
                _client.Close();
                _traceLog.Close();
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(true);
        }

        private void SetTraceLevel(TraceLevel tracingLevel)
        {
            AddFileTraceListener();
            AddNetTraceListener();
            AddNetStatus();

            Trace.AutoFlush = true;
            _TraceSwitch = new System.Diagnostics.TraceSwitch("MainSwitch", "The main switch");
            _TraceSwitch.Level = tracingLevel;
        }

        private void AddFileTraceListener()
        {
            Assembly epmAssembly = Assembly.GetExecutingAssembly();
            string fullPath = epmAssembly.Location;
            string directory = fullPath.Substring(0, fullPath.LastIndexOf(@"\") + 1);
            _currentFileName = String.Format(CultureInfo.InvariantCulture, "{0}{1} {2}.txt", directory, _coreFileName, 
                DateTime.Now.ToString("yyyyMMdd-HHmmss", DateTimeFormatInfo.InvariantInfo));

            _traceLog = new System.IO.FileStream(_currentFileName, System.IO.FileMode.OpenOrCreate);
            _fileTraceListener = new System.Diagnostics.TextWriterTraceListener(_traceLog);
            Trace.Listeners.Add(_fileTraceListener);
        }

        private void AddNetTraceListener()
        {
            _netTraceListener = new NetTraceListener(TraceConsts.NetTraceIP, TraceConsts.NetTracePort);
            Trace.Listeners.Add(_netTraceListener);
        }

        private void AddNetStatus()
        {
            _endPoint = new IPEndPoint(IPAddress.Parse(TraceConsts.NetTraceIP), TraceConsts.NetStatusPort);
            _client = new UdpClient();
        }

        private void StartNewFileIfPastSize(long fileSize)
        {
            FileInfo fi = new FileInfo(_currentFileName);
            if (fi.Length > fileSize)
            {
                Trace.Listeners.Remove(_fileTraceListener);
                _fileTraceListener.Close();
                _traceLog.Close();
                AddFileTraceListener();
            }
        }

        public TraceSwitch MainSwitch
        {
            get
            {
                return _TraceSwitch;
            }
        }

        public void WriteTrace(bool traceLevel, string output)
        {
            StackTrace stackTrace = new StackTrace();
            StackFrame stackFrame = stackTrace.GetFrame(1);
            MethodBase methodBase = stackFrame.GetMethod();

            Trace.WriteLineIf(traceLevel, String.Format(CultureInfo.InvariantCulture, "{0}.{1}, {2}, {3}",
                methodBase.ReflectedType, methodBase.Name, DateTime.Now.ToString("s", DateTimeFormatInfo.InvariantInfo), output));

            StartNewFileIfPastSize(TraceConsts.MaxLogFileSize);
        }

        public SyncState ProcessingState
        {
            get
            {
                return _processingState;
            }
            set
            {
                try
                {
                    if (_client != null)
                    {
                        string state = string.Empty;
                        switch (value)
                        {
                            case SyncState.Initializing:
                                state = "Initializing";
                                break;
                            case SyncState.Processing:
                                state = "Processing";
                                break;
                            case SyncState.Idle:
                                state = "Idle";
                                break;
                            case SyncState.Exiting:
                                state = "Exiting";
                                break;
                            case SyncState.Closed:
                                state = "Closed";
                                break;
                            case SyncState.Unknown:
                            default:
                                state = "Unknown";
                                break;

                        }
                        _processingState = value;
                        byte[] bytes = Encoding.ASCII.GetBytes(state);
                        _client.Send(bytes, bytes.Length, _endPoint);
                    }
                }
                catch (ArgumentNullException)
                {
                    WriteTrace(true, "Processing State Exception, ProcessingState Null");
                }
                catch (EncoderFallbackException)
                {
                    WriteTrace(true, "Processing State Exception, Encoder Fallback");
                }
                catch (ObjectDisposedException)
                {
                    WriteTrace(true, "Processing State Exception, UDP client was already closed");
                }
                catch (InvalidOperationException)
                {
                    WriteTrace(true, "Processing State Exception, Invalid Operation on UPD Send");
                }
                catch (SocketException)
                {
                    WriteTrace(true, "Processing State Exception, Socket Error");
                }

            }
        }

    }

}
